<?php
/**
 * Created by PhpStorm.
 * User: qch
 * Date: 2015/7/11
 * Time: 17:00
 */

namespace Aoe\Util\Bus;

use Closure;
use Exception;

/**
 * # 同步编译存储器
 *  > 用于数据读取、模板文件编译
 *
 */
class Convertor implements ISolid
{
    // const PASS_NULL  = 0;
    const int PASS_VALUE = 1;
    const int PASS_KEY   = 2;

    /**
     * @param ISolid                                          $persistence
     * @param null | ICache                                   $cache
     * @param Closure(mixed $value): mixed|null               $fnValue
     * @param Closure(string | int $key): (string | int)|null $fnKey
     * @param int                                             $pass          fnValue的参数传递方式
     */
    public function __construct(
        protected ISolid   $persistence,
        protected ?ICache  $cache,
        protected ?Closure $fnValue,
        protected ?Closure $fnKey,
        protected int      $pass = self::PASS_VALUE,
    ) {}

    public function get(int | string $key, $options = null): mixed
    {
        if ($options === false || !$this->cache) return $this->persistence->get($key, $options);
        return $this->cache->get($this->_sync_($key), $options);
    }

    /**
     * @throws Exception
     */
    private function _sync_(string $key): string
    {
        $cache_key = $this->_convert_key($key);

        if (!$this->cache->has($cache_key, $this->persistence->getExpire($key)))
            $this->cache->set($this->_convert_value($key), $cache_key);

        return $cache_key;
    }

    public function getExpire(int | string $key): false | int
    {
        return $this->persistence->getExpire($key);
    }

    public function set(mixed $value, int | string $key): int
    {
        $this->persistence->set($value, $key);
        $this->cache?->delete($key);
        return 1;
    }

    public function delete(int | array | string $keys): int
    {
        $this->cache?->delete($this->_convert_key($keys));
        $this->persistence->delete($keys);
        return $keys;
    }

    private function _convert_key(string | int | array $keys): string | int | array
    {
        if (is_array($keys)) return array_map($this->_convert_key(...), $keys);

        if (!$this->fnKey) return $keys;
        return call_user_func($this->fnKey, $keys);
    }

    /**
     * @throws Exception
     */
    private function _convert_value(string | int | null $key ): mixed
    {
        if (!$this->fnValue) return $this->persistence->get($key);
        if (!$this->pass) return call_user_func($this->fnValue);
        return ($this->fnValue)($this->pass === self::PASS_VALUE ? $this->persistence->get($key): $key);
    }
}